﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.WindowsAzure.StorageClient;
using Microsoft.WindowsAzure;
using System.Security;
using System.Diagnostics.Contracts;
using System.Diagnostics;

namespace CashFlow.Azure.Core
{
	public class Storage
	{
		private CloudTableClient tableClient;
		private CloudQueueClient queueClient;
		private CloudBlobClient blobClient;
		protected TableServiceContext db;

		private string endPoint;
		private CloudStorageAccount storageAccount;
		private RetryPolicy retryPolicy = RetryPolicies.Retry(3, TimeSpan.FromSeconds(1));

		public Storage() : this(CloudStorageAccount.DevelopmentStorageAccount)
		{
		}

		public Storage(CloudStorageAccount storageAccount)
		{
			Contract.Requires(storageAccount != null, "storageAccount cannot be null.");
			
			this.storageAccount = storageAccount;
			db = new TableServiceContext(storageAccount.TableEndpoint.ToString(), storageAccount.Credentials);
			tableClient = storageAccount.CreateCloudTableClient();
			blobClient = storageAccount.CreateCloudBlobClient();
			queueClient = storageAccount.CreateCloudQueueClient();

		}

		public bool CreateBlobContainer(string containerName)
		{
			Contract.Requires(!string.IsNullOrEmpty(containerName), "containerName cannot be empty.");
			Contract.Ensures(Contract.Result<bool>() == true);

			var result = true;

			try
			{
				var container = blobClient.GetContainerReference(containerName);
				result = container.CreateIfNotExist();
			}
			catch (Exception exc)
			{
				Trace.TraceError("{0}, {1}, {2}, {3}, {4}", this.GetType().FullName, System.Reflection.MethodBase.GetCurrentMethod().Name, "Exception", exc);
				throw;
			}			
			return result;
		}

		public bool CreateTable<T>(string tableName) where T : TableServiceEntity, new()
		{
			Contract.Requires(!string.IsNullOrEmpty(tableName), "tableName cannot be empty.");
			Contract.Ensures(Contract.Result<bool>() == true);

			var result = false;
			try
			{
				if (result = tableClient.CreateTableIfNotExist(tableName))
				{
					var ctx = tableClient.GetDataServiceContext();
					var dummyRow = new T
					{
						PartitionKey = "dummy",
						RowKey = "dummy"
					};
					ctx.AddObject(tableName, dummyRow);
					ctx.SaveChangesWithRetries();
					ctx.DeleteObject(dummyRow);
					ctx.SaveChangesWithRetries();
				}
			}
			catch (SecurityException secExc)
			{
				Trace.TraceError("{0}, {1}, {2}, {3}, {4}", this.GetType().FullName, System.Reflection.MethodBase.GetCurrentMethod().Name, "Sec exception", secExc);
				throw;
			}
			catch (Exception exc)
			{
				Trace.TraceError("{0}, {1}, {2}, {3}, {4}", this.GetType().FullName, System.Reflection.MethodBase.GetCurrentMethod().Name, "Exception", exc);
			}
			
			return result;
		}

		//TODO: vvs p3 refacor: combine with previous overload.
		public bool CreateTable(string tableName)
		{
			Contract.Requires(!string.IsNullOrEmpty(tableName), "tableName cannot be empty.");
			Contract.Ensures(Contract.Result<bool>() == true);

			var result = false;
			try
			{
				result = tableClient.CreateTableIfNotExist(tableName);
			}
			catch (SecurityException secExc)
			{
				Trace.TraceError("{0}, {1}, {2}, {3}, {4}", this.GetType().FullName, System.Reflection.MethodBase.GetCurrentMethod().Name, "Sec exception", secExc);
				throw;
			}
			catch (Exception exc)
			{
				Trace.TraceError("{0}, {1}, {2}, {3}, {4}", this.GetType().FullName, System.Reflection.MethodBase.GetCurrentMethod().Name, "Exception", exc);
			}

			return result;
		}

		public bool CreateQueue(string queueName)
		{
			Contract.Requires(!string.IsNullOrEmpty(queueName), "queueName cannot be empty.");
			Contract.Ensures(Contract.Result<bool>() == true);

			var result = true;
			try
			{
				var queue = queueClient.GetQueueReference(queueName);
				result = queue.CreateIfNotExist();
			}
			catch (Exception exc)
			{
				Trace.TraceError("{0}, {1}, {2}, {3}, {4}", this.GetType().FullName, System.Reflection.MethodBase.GetCurrentMethod().Name, "Exception", exc);
				throw;
			}			
			return result;
		}
	}
}
